#include "stdafx.h"
#include "cppflibinc.h"
#include "TestMiscFilePath.h"

using namespace cppflib;
using namespace cppflib::os;
using namespace cppflib::io;
using namespace cppflib::xml::sax;
using namespace cppflib::xml::dom;
using namespace cppflib::xml::writer;
using namespace cppflib::collections::tree;
using namespace cppflib::collections;

class MySAXHandler : public SAXHandler
{

private:
    void PrintIndentation()
    {
       //wprintf(_S("[%s]  "), static_cast<pcwstr_t>(this->GetCurFullTagName()));
       //wprintf(_S("%d:  "), this->GetNestedLevel());
       for (int i = 0; i < this->GetNestedLevel(); ++i)
          debug::PlainPrint(_S("  "));
    }
public:
    virtual void StartDocument() 
    {
       debug::PlainPrint(_S("[document started]\n"));
    }

    virtual void EndDocument()
    {
       debug::PlainPrint(_S("[document ended]\n"));
    }

    virtual void StartElement(const CFString &nameSpace, const CFString &fullTagName, 
                              const collections::LinkedHashtableT<CFString, CFString> &attributes)
    {
       PrintIndentation();

       debug::PlainPrint(_S("<%s"), static_cast<pcwstr_t>(fullTagName));

       if (attributes.GetCount() > 0) {
          printf(" ");
          const CFString *key = NULL;
          CFString *val = NULL;
          for (int i = 0; i < static_cast<int>(attributes.GetCount()); ++i) {
             attributes.Get(i, key, val);
             debug::PlainPrint(_S("%s=\"%s\""), static_cast<pcwstr_t>(*key), static_cast<pcwstr_t>(*val));
             if (i < static_cast<int>(attributes.GetCount()) - 1)
                printf(" ");
          }
       } 
       debug::PlainPrint(_S(">\n"));
    }

    virtual void ElementContent(const CFString &text)
    {
       
       CFString t(text);
       t.Trim(_S(" \t\r\n"));
       if (!t.IsEmpty()) {
          PrintIndentation();
          debug::PlainPrint(_S("%s\n"), static_cast<pcwstr_t>(t));
       }
    }

    virtual void EndElement(const CFString &nameSpace, const CFString &fullTagName)
    {
       PrintIndentation();
       debug::PlainPrint(_S("</%s>\n"), static_cast<pcwstr_t>(fullTagName));
    }

    virtual void Error(const CFString &msg)
    {
       debug::PlainPrint(_S("error: %s\n"), static_cast<pcwstr_t>(msg));
    }

    virtual void FatalError(const CFString &msg)
    {
       debug::PlainPrint(_S("fatal: %s\n"), static_cast<pcwstr_t>(msg));
    }

    virtual void Warning(const CFString &msg)
    {
       debug::PlainPrint(_S("warning: %s\n"), static_cast<pcwstr_t>(msg));
    }

};

class EmptySAXHandler : public SAXHandler
{
public:
    virtual void StartDocument() 
    {}

    virtual void EndDocument()
    {}

    virtual void StartElement(const CFString &nameSpace, const CFString &fullTagName, 
                              const collections::LinkedHashtableT<CFString, CFString> &attributes)
    {}

    virtual void ElementContent(const CFString &text)
    {}

    virtual void EndElement(const CFString &nameSpace, const CFString &fullTagName)
    {}

    virtual void Error(const CFString &msg)
    {
       //debug::PlainPrint(_S("error: %s\n"), static_cast<pcwstr_t>(msg));
    }

    virtual void FatalError(const CFString &msg)
    {
       //debug::PlainPrint(_S("fatal: %s\n"), static_cast<pcwstr_t>(msg));
    }

    virtual void Warning(const CFString &msg)
    {
       //debug::PlainPrint(_S("warning: %s\n"), static_cast<pcwstr_t>(msg));
    }

};

/**
 *  Node to aggregate contents of a XML node
 */
class SAXTreeNode : public MCTreeNode
{
private:
    CFString *textContent;
    collections::LinkedHashtableT<CFString, CFString> *attrs;

private:
    SAXTreeNode() { }

public:
    explicit SAXTreeNode(CFString *pKey) : MCTreeNode(pKey)
    {
       textContent = NULL;
       attrs = NULL;
    }

    virtual ~SAXTreeNode()
    {
       if (textContent)
          cppmempool::Release(textContent);

       if (attrs)
          cppmempool::Release(attrs);
    } 

    CFString * GetText()
    {
       return textContent;
    }

    void AppendText(const CFString &text)
    {
       if (!textContent) {
          textContent = new CFString();
          cppmempool::Retain(textContent);
       }
       *textContent += text;
       textContent->Trim();
    }

    collections::LinkedHashtableT<CFString, CFString> * GetAttributes()
    {
       return attrs;
    }

    void SetAttributes(const collections::LinkedHashtableT<CFString, CFString> & attributes)
    {
       if (attrs) {
          cppmempool::Release(attrs);
       }
       else {
          attrs = attributes.Copy();
          cppmempool::Retain(attrs);
       }
    }

    virtual bool AddChild(MCTreeNode * pChild)
    { 
       if (cppflib::InstanceOf<SAXTreeNode>(pChild)) {
          MCTreeNode::AddChild(pChild);
          return true;
       }

       throw exceptions::RuntimeException(_S("argument is not an instance of SAXTreeNode"));
    }

    virtual bool DeleteChild(const BaseObject &key)
    {  
       if (cppflib::InstanceOf<CFString>(&key)) {
          return MCTreeNode::DeleteChild(key);
       }

       throw exceptions::RuntimeException(_S("key is not an instance of CFString"));
       return false;
    }

    virtual bool Move(MCTreeNode *pNode)
    { 
       if (cppflib::InstanceOf<SAXTreeNode>(pNode)) {
          return MCTreeNode::Move(pNode);
       }

       throw exceptions::RuntimeException(_S("argument is not an instance of SAXTreeNode"));
       return false;
    }

};

/**
 *  XML tree
 */
class SAXMCTree : public MultiChildTree
{

public:
    SAXMCTree() : MultiChildTree(false) // allow duplicated names in children
    {
    }

    virtual void SetRootNode(MCTreeNode * pRootNode)
    {
       if (cppflib::InstanceOf<SAXTreeNode>(pRootNode)) {
          MultiChildTree::SetRootNode(pRootNode);
          return;
       }

       throw exceptions::RuntimeException(_S("argument is not an instance of SAXTreeNode"));
    }
};

/**
 *  SAX Handler to save contents in a SAXMCTree
 */
class MCTreeSAXHandler : public SAXHandler
{
private:
    SAXMCTree tree;
    SAXTreeNode * pCurNode;

private:
    static void Treewalkcb(MCTreeNode & node)
    {
       SAXTreeNode *pNode = static_cast<SAXTreeNode*>(&node);
       CFString s;
       for (u32_t i = 0; i < pNode->GetDepth(); ++i)
          s.AppendFormat(_S("  "));
           
       s += *static_cast<const CFString*>(pNode->GetKey());
       collections::LinkedHashtableT<CFString, CFString> * attrs = pNode->GetAttributes();
       if (pNode->GetText() && ! pNode->GetText()->IsEmpty())
          s.AppendFormat(_S(" [%s] "), static_cast<pcwstr_t>(*pNode->GetText()));

       if (attrs && attrs->GetCount() > 0) {
          s += _S(": ");
          const CFString *pKey;
          CFString *pVal;
          for (u32_t i = 0; i < attrs->GetCount(); ++i) {
             attrs->Get(static_cast<int>(i), pKey, pVal);
             s.AppendFormat(_S("%s=%s "), pKey->ToString(), pVal->ToString());
          }
       }

       debug::Print(debug::DL_DEBUG, _S("%s\n"), static_cast<pcwstr_t>(s));
    }

public:
    MCTreeSAXHandler()
    {
       pCurNode = NULL;
    }

    virtual void StartDocument() 
    {}

    virtual void EndDocument()
    {
       //tree.TraverseTree(MCTreeWalkCallback(&Treewalkcb));
    }

    virtual void StartElement(const CFString &nameSpace, const CFString &fullTagName, 
                              const collections::LinkedHashtableT<CFString, CFString> &attributes)
    {
       SAXTreeNode *pNode = new SAXTreeNode(new CFString(fullTagName));
       pNode->SetAttributes(attributes);

       if (tree.IsTreeEmpty())
          tree.SetRootNode(pNode);
       else
          pCurNode->AddChild(pNode);

       pCurNode = pNode;
    }

    virtual void ElementContent(const CFString &text)
    {
       if (pCurNode)
          pCurNode->AppendText(text);
    }

    virtual void EndElement(const CFString &nameSpace, const CFString &fullTagName)
    {
       if (pCurNode)
          pCurNode = static_cast<SAXTreeNode*>(pCurNode->GetParent()); // go back to parent
    }

    virtual void Error(const CFString &msg)
    {
       //debug::PlainPrint(_S("error: %s\n"), static_cast<pcwstr_t>(msg));
    }

    virtual void FatalError(const CFString &msg)
    {
       //debug::PlainPrint(_S("fatal: %s\n"), static_cast<pcwstr_t>(msg));
    }

    virtual void Warning(const CFString &msg)
    {
       //debug::PlainPrint(_S("warning: %s\n"), static_cast<pcwstr_t>(msg));
    }

};

static FileStruct xmlFiles[] = {
                        {XML_PATH_PREFIX _S("hello_big_1.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_big_2.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_big_3.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_big_4.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_ns_1.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_ns_2.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_ns_3.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_2.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_3.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_4.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_5.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_6.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_7.xml"), false, true}, 
                        {XML_PATH_PREFIX _S("hello_8.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_9.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_10.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_10_a.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_11.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_11_a.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_12.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_13.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_14.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_15.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_16.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_16_a.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_17.xml"), true, true}, 
                        {XML_PATH_PREFIX _S("hello_18.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_18_a.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_19.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_20.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_21.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_22.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_23.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_24.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_25.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_26.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_27.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_28.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_29.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_30.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_31.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_32.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_33.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_34.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_35.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_36.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_37.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_38.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_39.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_40.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_41.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_42.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_43.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_44.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_45.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_46.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_47.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_48.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_49.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_50.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_51.xml"), false, true}, 
                        {XML_PATH_PREFIX _S("hello_52.xml"), false, true},
                        {XML_PATH_PREFIX _S("hello_53.xml"), true, true},
                        {XML_PATH_PREFIX _S("hello_54.xml"), false, true},
                        {XML_PATH_PREFIX _S("bigxml.xml"), true, false},
                        {NULL, false, false}
                      };

FileStruct * GetXmlFiles()
{
   return xmlFiles;
}

void ConfigXmlFileList(bool isFullTest)
{
   if (!isFullTest)
      return;

   for (int i = 0; xmlFiles[i].filePath; ++i) {
      if (!xmlFiles[i].involveInFullTest)
         xmlFiles[i].involveInFullTest = true;
   }
}

static void TestParseXml()
{
   cppmempool::MemPoolWrap memPool;

   CFString fileName;
   SAXParser parser;

   for (int i = 0; xmlFiles[i].filePath && xmlFiles[i].involveInFullTest; ++i) {
      fileName = xmlFiles[i].filePath;
      SAXHandler *handler = new EmptySAXHandler();
/*
      if (fileName.Find(_S("hello_11_a.xml")) >= 0) 
         handler = new MySAXHandler();
*/
      parser.Parse(xmlFiles[i].filePath, *handler);
      if (handler->IsErrorMarked() != !xmlFiles[i].shouldParseOK) {
         debug::Print(debug::DL_INFO, _S("Error parsing %s\n"), xmlFiles[i].filePath);
         assert(false);
      }
   }

   debug::Print(_S("Parse XML files end...\n"));
}

static void TestParseXml_2()
{
   cppmempool::MemPoolWrap memPool;

   debug::Print(_S("Test parsing XML and saving in a MCTree ...\n"));
   SAXParser parser;
   SAXHandler *handler = new MCTreeSAXHandler();
   parser.Parse(xmlFiles[0].filePath, *handler);
}

static void TestParseXml_3()
{
   cppmempool::MemPoolWrap memPool;
   debug::Print(_S("Test parse XML using Document ...\n"));

   try {
      Document *pDoc = Document::Parse(XML_PATH_PREFIX _S("hello_big_1.xml"));
      Node *pNode = pDoc->GetNodeByTagName(_S("book"));
      assert(pNode != NULL);
      assert(pNode->GetNodeName()->Compare(_S("book")) == 0);
      assert(pNode->GetDirectChildCount() == 6);
      assert(pNode->GetAllChildCount() == 6);
      assert(pNode->HasAttributes());
      const collections::LinkedHashtableT<CFString, CFString> *pAttrs = pNode->GetAttributes();
      assert(*pAttrs->Get(CFString(_S("id"))) == _S("bk101"));

      Node *pSubNode = pNode->GetChildNodeByTagName(_S("genre"));
      assert(pSubNode != NULL);
      assert(pSubNode->GetDirectChildCount() == 0);
      assert(*pSubNode->GetNodeTextContent() == _S("Computer"));
      assert(!pSubNode->HasAttributes());
      assert(pSubNode->GetParent() == pNode);
      Node *pPrev = static_cast<Node*>(pSubNode->GetPrevSibling());
      assert(*pPrev->GetNodeName() == _S("title"));
      Node *pNext = static_cast<Node*>(pSubNode->GetNextSibling());
      assert(*pNext->GetNodeName() == _S("price"));
      

      pNode = pDoc->GetNodeByTagName(_S("book/genre"));
      assert(pNode == pSubNode);  // OK
      pNode = NULL;

      pNode = pDoc->GetNodeByTagName(_S("book//genre"));
      assert(pNode == pSubNode);  // OK
      pNode = NULL;

      pNode = pDoc->GetNodeByTagName(_S("/book/genre"));
      assert(pNode == pSubNode);  // OK
      pNode = NULL;

      pNode = pDoc->GetNodeByTagName(_S("/book/ /genre"));
      assert(pNode == NULL);  // FAIL

      pNode = pDoc->GetNodeByTagName(_S("//book/////genre"));
      assert(pNode == pSubNode);  // OK

      pNode = pDoc->GetNodeByTagName(_S("books"));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S("book/RTRET"));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S("book/genre/"));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S("book/genre/DDD"));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S(""));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S("/"));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S(" / / / "));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(_S(" /// "));
      assert(pNode == NULL);

      pNode = pDoc->GetNodeByTagName(NULL);
      assert(pNode == NULL);

      int count = 1;
      pNode = pDoc->GetNodeByTagName(_S("book"));
      while(pNode != NULL) {
         assert(*pNode->GetNodeName() == _S("book"));
         pNode = static_cast<Node*>(pNode->GetNextSibling());
         if (pNode != NULL)
            ++count;
      }
      assert(count == 12);
   }
   catch(exceptions::BaseException & e) {
      debug::Print(debug::DL_ERROR, _S("Error getting document -- %s\n"), e.GetMsg());
      assert(false);
   }
}

static void TestParseXml_4()
{
   cppmempool::MemPoolWrap memPool;
   debug::Print(_S("Test parsing erroneous XML using Document ...\n"));

   CFString fileName;

   for (int i = 0; xmlFiles[i].filePath && xmlFiles[i].involveInFullTest; ++i) {
      fileName = xmlFiles[i].filePath;

      if (xmlFiles[i].shouldParseOK) {
         Document *pDoc = Document::Parse(fileName);  // should be OK
      }
      else {
         try {
            Document *pDoc = Document::Parse(fileName);  // should fail
            debug::Print(debug::DL_ERROR, _S("Error should occur when parsing document -- %s\n"), static_cast<pcwstr_t>(fileName));
            assert(false);
         }
         catch(DOMException &e) {
            // OK
            e.GetMsg(); // to eliminate compiler warning
         }
         catch(exceptions::BaseException & e) {
            debug::Print(debug::DL_ERROR, _S("What is this exception ? -- %s (%s)\n"), static_cast<pcwstr_t>(fileName), e.GetMsg());
            assert(false);
         }
      }
   }
}

//#define _WRITETOSTRING

static Stream * __TestWriteXml()
{
   cppmempool::MemPoolWrap memPool;

   xmlwritersettings_t settings = XmlWriter::GetDefaultXmlWriterSettings();
   settings.closeOutputWriter = false;
   settings.indent = true;
   settings.charSet = text::CS_UTF8;
   settings.includeXmlDeclaration = true;

#ifdef _WRITETOSTRING
   StringWriter * pWriter = new StringWriter(256);
#else
   MemoryStream * pStream = new MemoryStream();
   StreamWriter * pWriter = new StreamWriter(pStream, text::CS_UTF8);
#endif
   XmlWriter * pXmlWriter = new XmlWriter(pWriter, settings);

   try {
      
      pXmlWriter->WriteStartElement(CFString("HelloRoot"))
                    .WriteStartElement(CFString("Indent_1"))
                       .WriteStartElement(CFString("Indent_2"))
                          .WriteStartElement(CFString("Indent_3"))
                             .WriteStartElement(CFString("sub_1")) 
                             .WriteAttribute(CFString("attr1"), CFString("value1"))   
                             .WriteAttribute(CFString("attr2"), CFString("value2")) 
                             .WriteElementContent(CFString("Just a piece of content. Haha ... |&,>,<,',\"|")) 
                             .WriteCDATA(CFString("string inside CDATA ....<><>"));
      ArrayWChar charBuf(27);
      for (int i = 0; i < static_cast<int>(charBuf.GetSize()); ++i)
         charBuf[i] = static_cast<wc_t>(_S('A') + i);
      pXmlWriter->WriteElementContent(charBuf)
                 .WriteEndElement();

      CFString ts("content of gogogo");
#ifndef _WRITETOSTRING
      ts += static_cast<wc_t>(0xd85f); // write UTF16 surrogate pairs
      ts += static_cast<wc_t>(0xdd44);
#endif
      pXmlWriter->WriteElement(CFString("gogogo"), ts)
                 .WriteStartElement(CFString("sub_1"))
                 .WriteAttribute(CFString("gogo1"), CFString("qqqqwwww"))
                 .WriteComment(CFString("Hello comment string"))
                 .WriteComment(CFString("Another comment string"));

      ArrayByte buffer(27);
      for (int i = 0; i < static_cast<int>(buffer.GetSize()); ++i)
         buffer[i] = static_cast<u8_t>('a' + i);
      pXmlWriter->WriteBase64(buffer, 0, static_cast<int>(buffer.GetSize()))
                 .WriteEndElement()
                 .WriteStartElement(CFString("empty_element"))
                 .WriteFullEndElement();
      pXmlWriter->WriteEndDocument();
      pXmlWriter->Close();

#ifdef _WRITETOSTRING
      debug::PlainPrint(pWriter->GetStringBuffer());
#endif

   }
   catch(XmlWriterException &e) {
      debug::Print(debug::DL_ERROR, _S("XmlWriter error -- %s"), e.GetMsg());
      assert(false);
   }

#ifdef _WRITETOSTRING
   return NULL;
#else
   pStream->ResetPointer(); // reset to beginning
   cppmempool::UnlinkFromPool(pStream); // return pStream, unlink from current pool
   return pStream;
#endif
   
}

static void TestWriteXml()
{
   cppmempool::MemPoolWrap memPool;

#ifdef _WRITETOSTRING
   __TestWriteXml();
#else
   Stream *pStream = __TestWriteXml();
   memPool.AddMemBlock(pStream);
   
   try {
      Document *pDoc = Document::Parse(*pStream);  // should be OK
   }
   catch(exceptions::BaseException & e) {
      debug::Print(debug::DL_ERROR, _S("What is this exception ? (%s)\n"), e.GetMsg());
      assert(false);
   }
#endif

}

static void TestWriteXml_2()
{
   cppmempool::MemPoolWrap memPool;

   MemoryStream * pStream = new MemoryStream();
   StreamWriter * pWriter = new StreamWriter(pStream, text::CS_UTF8);
   XmlWriter * pXmlWriter = new XmlWriter(pWriter);

   try {
      pXmlWriter->WriteComment(CFString("This is comment")); // only StartElement is allowed
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteElementContent(CFString("hello world")); // only StartElement is allowed
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteStartElement(CFString("Root")); // StartElement
   }
   catch(XmlWriterException &e) { 
      e.GetMsg(); 
      assert(false); 
   }

   try {
      pXmlWriter->WriteElementContent(CFString("Some text here"));
   }
   catch(XmlWriterException &e) {  
      e.GetMsg(); 
      assert(false); 
   }

   try {
      pXmlWriter->WriteAttribute(CFString("attr"), CFString("value")); // can't write attribute now
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteStartElement(CFString("subNode"));
      pXmlWriter->WriteAttribute(CFString("attr"), CFString("value"));
      pXmlWriter->WriteAttribute(CFString("attr"), CFString("value"));  // duplicated attribute
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteElement(CFString("mmNode"), CFString("mmValue"));
   }
   catch(XmlWriterException &e) { 
      e.GetMsg(); 
      assert(false); 
   }

   try {
      pXmlWriter->WriteEndElement();
   }
   catch(XmlWriterException &e) {  
      e.GetMsg(); 
      assert(false); 
   }  

   try {
      pXmlWriter->WriteStartElement(CFString("anotherNode"));
      pXmlWriter->WriteAttribute(CFString("attr"), CFString("value"));
      pXmlWriter->WriteAttribute(CFString("newAttr"), CFString("value"));
      pXmlWriter->WriteAttribute(CFString("newAttr"), CFString("value")); // duplicated attribute
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteEndDocument();
   }
   catch(XmlWriterException &e) { 
      e.GetMsg(); 
      assert(false); 
   }

   /////////////////////////////////////  document ends /////////////////////////////////////////

   try {
      pXmlWriter->WriteElement(CFString("outbound"), CFString("value")); // no more node after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteEndElement(); // no more node after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteAttribute(CFString("attr"), CFString("value")); // no more node after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteElementContent(CFString("content")); // no more node after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg(); }

   try {
      pXmlWriter->WriteFullEndElement(); // no more node after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg();  }

   try {
      pXmlWriter->WriteComment(CFString("comment")); // no more comment after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg();  }

   try {
      pXmlWriter->WriteCDATA(CFString("CDATA")); // no more CDATA after document ends
      assert(false);
   }
   catch(XmlWriterException &e) { e.GetMsg();  }

   try {
      pXmlWriter->WriteEndDocument();  // end twice cause no harm
      pXmlWriter->Flush();
      pXmlWriter->Close();
   }
   catch(XmlWriterException &e) { 
      e.GetMsg(); 
      assert(false); 
   }

   pStream->ResetPointer();

   // parse the stream finally
   try {
      Document *pDoc = Document::Parse(*pStream);  // should be OK
   }
   catch(exceptions::BaseException & e) {
      debug::Print(debug::DL_ERROR, _S("What is this exception ? (%s)\n"), e.GetMsg());
      assert(false);
   }
}

void TestXml(void)
{
   debug::Print(_S("Testing XML parsers...\n"));
   TestParseXml();
   TestParseXml_2();
   TestParseXml_3();
   TestParseXml_4();

   debug::Print(_S("Testing XML writer...\n"));
   TestWriteXml();
   TestWriteXml_2();
}
